package com.google.code.mamule.process;

import com.google.code.mamule.MamuleException;
import com.google.code.mamule.delivery.DeliveryCallback;
import com.google.code.mamule.delivery.MailDelivery;
import com.google.code.mamule.mail.Mail;
import com.google.code.mamule.mail.MailRepository;
import com.google.code.mamule.mail.MailStatus;
import com.google.code.mamule.order.Order;
import com.google.code.mamule.order.OrderProcessor;
import com.google.code.mamule.print.MailPrinter;
import com.google.code.mamule.print.PrintDocument;
import com.google.code.mamule.print.PrinterCallback;
import com.google.common.collect.Iterables;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.Iterator;

/**
 * Created with IntelliJ IDEA. User: borck_000 ; Date: 12/24/13 ; Time: 2:25 PM
 */
@Service
@Transactional
public class MamuleService implements OrderProcessor, PrinterCallback, DeliveryCallback {

  private static final Logger log = LoggerFactory.getLogger(MamuleService.class);

  private final MailRepository mails;

  private final MailRenderer renderer;
  private final MailDelivery delivery;
  private final MailPrinter printer;

  @Autowired
  public MamuleService(final MailRepository mails, final MailRenderer renderer,
                       final MailDelivery delivery,
                       final MailPrinter printer) {
    this.mails = mails;
    this.renderer = renderer;
    this.delivery = delivery;
    this.printer = printer;
  }

  @Override
  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void process(final Order it) {
    if (it.getState() != Order.State.TRANSIENT) {
      throw new MamuleException("cannot process order : order is not open");
    }
    Iterable<PrintDocument> rendered =
        Iterables.concat(Iterables.transform(it.getItems(), renderer));
    Iterator<Mail> mails = it.getItems().iterator();
    Iterator<PrintDocument> docs = rendered.iterator();
    for (; mails.hasNext() && docs.hasNext(); ) {
      Mail each = mails.next();
      log.info("submitting mail : {}", each);
      printer.print(each.getId(), docs.next());
      each.setStatus(MailStatus.SUBMITTED);
    }
    log.info("confirmed order : {}", it);
    it.setState(Order.State.CONFIRMED);
  }

  @Override
  public void onPrinted(final Long id) {
    log.debug("{} printed", id);
    Mail printed = mails.findOne(id);
    if (printed != null) {
      delivery.announceMailForDelivery(Collections.singleton(printed));
    } else {
      log.error("received print confirmation for non existing mail {}", id);
    }
  }

  @Override
  public void onMailSent(final Long id) {
    log.debug("{} sent", id);
    Mail sent = mails.findOne(id);
    if (sent != null) {
      sent.setStatus(MailStatus.SENT);
    } else {
      log.error("received send confirmation for non existing mail {}", id);
    }
  }

  @Override
  public void onMailDelivered(final Long id) {
    log.debug("{} sent", id);
    Mail sent = mails.findOne(id);
    if (sent != null) {
      sent.setStatus(MailStatus.DELIVERED);
    } else {
      log.error("received delivered confirmation for non existing mail {}", id);
    }
  }
}
